/*
REWRITTEN FROM SCRATCH BY XINEF, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
#include "SpellScript.h"
#include "Player.h"

enum Says
{
	// Stalagg
    SAY_STAL_AGGRO          = 0,
    SAY_STAL_SLAY           = 1,
    SAY_STAL_DEATH          = 2,

	// Feugen
    SAY_FEUG_AGGRO          = 0,
    SAY_FEUG_SLAY           = 1,
    SAY_FEUG_DEATH          = 2,

	// Thaddius
    SAY_GREET               = 0,
    SAY_AGGRO               = 1,
    SAY_SLAY                = 2,
    SAY_ELECT               = 3,
    SAY_DEATH               = 4,
    SAY_SCREAM              = 5
};

enum Spells
{
	SPELL_MAGNETIC_PULL					= 28337,
	SPELL_TESLA_SHOCK					= 28099,

	// Stalagg
	SPELL_POWER_SURGE_10				= 54529,
	SPELL_POWER_SURGE_25				= 28134,
	SPELL_STALAGG_CHAIN					= 28096,

	// Feugen
	SPELL_STATIC_FIELD_10				= 28135,
    SPELL_STATIC_FIELD_25				= 54528,
	SPELL_FEUGEN_CHAIN					= 28111,

	// Thaddius
	SPELL_POLARITY_SHIFT				= 28089,
    SPELL_BALL_LIGHTNING				= 28299,
    SPELL_CHAIN_LIGHTNING_10			= 28167,
    SPELL_CHAIN_LIGHTNING_25			= 54531,
    SPELL_BERSERK						= 27680,

	SPELL_THADDIUS_VISUAL_LIGHTNING		= 28136,
	SPELL_THADDIUS_SPAWN_STUN			= 28160,

	SPELL_POSITIVE_CHARGE				= 28062,
    SPELL_POSITIVE_CHARGE_STACK			= 29659,
    SPELL_NEGATIVE_CHARGE				= 28085,
    SPELL_NEGATIVE_CHARGE_STACK			= 29660,
    SPELL_POSITIVE_POLARITY				= 28059,
    SPELL_NEGATIVE_POLARITY				= 28084,
};

enum Events
{
	EVENT_MINION_SPELL_POWER_SURGE		= 1,
	EVENT_MINION_SPELL_MAGNETIC_PULL	= 2,
	EVENT_MINION_CHECK_DISTANCE			= 3,
	EVENT_MINION_SPELL_STATIC_FIELD		= 4,

	EVENT_THADDIUS_START				= 10,
	EVENT_THADDIUS_SPELL_CHAIN_LIGHTNING= 11,
	EVENT_THADDIUS_SPELL_BERSERK		= 12,
	EVENT_THADDIUS_POLARITY_SHIFT		= 13,
	EVENT_THADDIUS_START_2				= 14,
};

enum Misc
{
	ACTION_MAGNETIC_PULL				= 1,
	ACTION_SUMMON_DIED					= 2,
	ACTION_RESTORE						= 3,
	NPC_TESLA_COIL						= 16218,           //the coils (emotes "Tesla Coil overloads!")
};

class boss_thaddius : public CreatureScript
{
public:
    boss_thaddius() : CreatureScript("boss_thaddius") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_thaddiusAI (pCreature);
    }

	struct boss_thaddiusAI : public ScriptedAI
	{
		boss_thaddiusAI(Creature *c) : ScriptedAI(c), summons(me)
		{
			pInstance = me->GetInstanceScript();
		}

		InstanceScript* pInstance;
		EventMap events;
		SummonList summons;
		uint32 summonTimer;
		uint32 reviveTimer;
		uint32 resetTimer;

		void StartEvent()
		{
			me->RemoveAllAuras();
			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
			events.ScheduleEvent(EVENT_THADDIUS_START_2, 1000);
			for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
				if (Creature* cr = ObjectAccessor::GetCreature(*me, (*itr)))
					if (cr->GetEntry() == NPC_TESLA_COIL)
					{
						cr->CastSpell(me, SPELL_TESLA_SHOCK, true); // till smth better is found ;( ZOMG
						Unit::Kill(cr, cr);
					}
		}

		void DoAction(int32 param)
		{
			if (param == ACTION_SUMMON_DIED)
			{
				// Hooray, we did it!
				if (summonTimer)
				{
					summonTimer = 0;
					reviveTimer = 1;
					//events.ScheduleEvent(EVENT_THADDIUS_START, 12000);
					return;
				}

				summonTimer = 1;
			}
		}

		void Reset() 
		{
			events.Reset();
			summons.DespawnAll();
			me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
			summonTimer = 0;
			reviveTimer = 0;
			resetTimer = 1;
			me->SetPosition(me->GetHomePosition());

			if (pInstance) 
				pInstance->SetData(EVENT_THADDIUS, NOT_STARTED);

			me->SummonCreature(NPC_STALAGG, 3450.45f, -2931.42f, 312.091f, 5.49779f);
			me->SummonCreature(NPC_FEUGEN, 3508.14f, -2988.65f, 312.092f, 2.37365f);
			if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3527.34f, -2951.56f, 318.75f, 0.0f))
			{
				cr->RemoveAllAuras();
				cr->InterruptNonMeleeSpells(true);
				cr->CastSpell(cr, SPELL_FEUGEN_CHAIN, false);
				cr->SetDisableGravity(true);
			}
			if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3487.04f, -2911.68f, 318.75f, 0.0f))
			{
				cr->RemoveAllAuras();
				cr->InterruptNonMeleeSpells(true);
				cr->CastSpell(cr, SPELL_STALAGG_CHAIN, false);
				cr->SetDisableGravity(true);
			}
		}

		void KilledUnit(Unit* who)
		{
			if (who->GetTypeId() != TYPEID_PLAYER)
				return;

			if (!urand(0,3))
				Talk(SAY_SLAY);

			if (pInstance)
				pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
		}

		void JustDied(Unit* Killer)
		{
			Talk(SAY_DEATH);
			if (pInstance)
			{
				pInstance->SetData(EVENT_THADDIUS, DONE);
				pInstance->DoRemoveAurasDueToSpellOnPlayers(28059);
				pInstance->DoRemoveAurasDueToSpellOnPlayers(28084);
			}
		}

		void JustSummoned(Creature* cr) { summons.Summon(cr); }

		void EnterCombat(Unit *who)
		{
			me->SetInCombatWithZone();
			summons.DoZoneInCombat(NPC_FEUGEN);
			summons.DoZoneInCombat(NPC_STALAGG);

			if (pInstance)
				pInstance->SetData(EVENT_THADDIUS, IN_PROGRESS);
		}

		void UpdateAI(uint32 diff)
		{
			if (resetTimer)
			{
				resetTimer += diff;
				if (resetTimer > 1000)
				{
					resetTimer = 0;
					me->CastSpell(me, SPELL_THADDIUS_SPAWN_STUN, true);
				}
				return;
			}
			if (reviveTimer)
			{
				reviveTimer += diff;
				if (reviveTimer >= 12000)
				{
					StartEvent();
					reviveTimer = 0;
				}
				return;
			}

			if (!UpdateVictim())
			   return;

			events.Update(diff);
			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			// Revive code!
			if (summonTimer)
			{
				summonTimer += diff;
				if (summonTimer >= 5000)
				{
					summons.DoAction(ACTION_RESTORE);
					summonTimer = 0;
				}
			}

			switch (events.GetEvent())
			{
				//case EVENT_THADDIUS_START:
				//	StartEvent();
				//	events.PopEvent();
				//	break;
				case EVENT_THADDIUS_START_2:
					events.PopEvent();
					Talk(SAY_AGGRO);
					me->SetReactState(REACT_AGGRESSIVE);
					me->SetControlled(false, UNIT_STATE_STUNNED);
					me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
					me->setAttackTimer(BASE_ATTACK, 4000);
					
					events.ScheduleEvent(EVENT_THADDIUS_SPELL_CHAIN_LIGHTNING, 14000);
					events.ScheduleEvent(EVENT_THADDIUS_SPELL_BERSERK, 360000);
					events.ScheduleEvent(EVENT_THADDIUS_POLARITY_SHIFT, 30000);
					return;
				case EVENT_THADDIUS_SPELL_BERSERK:
					me->CastSpell(me, SPELL_BERSERK, true);
					events.PopEvent();
					break;
				case EVENT_THADDIUS_SPELL_CHAIN_LIGHTNING:
					me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_CHAIN_LIGHTNING_10, SPELL_CHAIN_LIGHTNING_25), false);
					events.RepeatEvent(15000);
					break;
				case EVENT_THADDIUS_POLARITY_SHIFT:
					me->CastSpell(me, SPELL_POLARITY_SHIFT, false);
					events.RepeatEvent(30000);
					break;
			}

			if (me->isAttackReady())
			{
				if (!me->IsWithinMeleeRange(me->GetVictim()))
					me->CastSpell(me->GetVictim(), SPELL_BALL_LIGHTNING, false);
				else
					DoMeleeAttackIfReady();
			}
		}
	};
};

class boss_thaddius_summon : public CreatureScript
{
public:
	boss_thaddius_summon() : CreatureScript("boss_thaddius_summon") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new boss_thaddius_summonAI (pCreature);
	}

	struct boss_thaddius_summonAI : public ScriptedAI
	{
		boss_thaddius_summonAI(Creature *c) : ScriptedAI(c) 
		{
		   pInstance = me->GetInstanceScript();
		}

		InstanceScript* pInstance;
		EventMap events;
		uint32 pullTimer;
		uint32 visualTimer;

		void Reset()
		{
			pullTimer = 0;
			visualTimer = 1;
			
			events.Reset();
			me->SetControlled(false, UNIT_STATE_STUNNED);
			if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
				cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
		}

		void EnterEvadeMode()
		{
		   me->SetControlled(false, UNIT_STATE_STUNNED);
		   ScriptedAI::EnterEvadeMode();
		}

		void EnterCombat(Unit* pWho)
		{
			me->SetInCombatWithZone();

			if (me->GetEntry() == NPC_STALAGG)
			{
				events.ScheduleEvent(EVENT_MINION_SPELL_POWER_SURGE, 10000);
				Talk(SAY_STAL_AGGRO);
			}
			else
			{
				events.ScheduleEvent(EVENT_MINION_SPELL_STATIC_FIELD, 5000);
				Talk(SAY_FEUG_AGGRO);
			}
			
			events.ScheduleEvent(EVENT_MINION_CHECK_DISTANCE, 5000);

			// This event needs synchronisation, called for stalagg only
			if (me->GetEntry() == NPC_STALAGG)
				events.ScheduleEvent(EVENT_MINION_SPELL_MAGNETIC_PULL, 25000);

			if (pInstance)
				if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetData64(DATA_THADDIUS_BOSS)))
				{
					cr->AI()->AttackStart(pWho);
					cr->AddThreat(pWho, 10.0f);
				}
		}

		void DoAction(int32 param)
		{
			if (param == ACTION_MAGNETIC_PULL)
			{
				pullTimer = 1;
				me->SetControlled(true, UNIT_STATE_STUNNED);
			}
			else if (param == ACTION_RESTORE)
			{
				if (!me->IsAlive())
				{
					me->Respawn();
					me->SetInCombatWithZone();
				}
				else
					me->SetHealth(me->GetMaxHealth());
			}
		}

		void JustDied(Unit* )
		{
			Talk(me->GetEntry() == NPC_STALAGG ? SAY_FEUG_DEATH : SAY_STAL_DEATH);
			if (pInstance)
				if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetData64(DATA_THADDIUS_BOSS)))
					cr->AI()->DoAction(ACTION_SUMMON_DIED);
		}

		void KilledUnit(Unit* who)
		{
			if (who->GetTypeId() != TYPEID_PLAYER)
				return;

			if (pInstance)
				pInstance->SetData(DATA_IMMORTAL_FAIL, 0);

			if (!urand(0,2))
				Talk(me->GetEntry() == NPC_STALAGG ? SAY_FEUG_SLAY : SAY_STAL_SLAY);
		}

		void UpdateAI(uint32 diff)
		{
			if (visualTimer)
			{
				visualTimer += diff;
				if (visualTimer >= 3000)
				{
					visualTimer = 0;
					if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
						cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
				}
			}

			if (!UpdateVictim())
				return;

			// Disable AI during pull
			if (pullTimer)
			{
				pullTimer += diff;
				if (pullTimer >= 3000)
				{
					me->SetControlled(false, UNIT_STATE_STUNNED);
					pullTimer = 0;
				}
				return;
			}

			events.Update(diff);
			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (events.GetEvent())
			{
				case EVENT_MINION_SPELL_POWER_SURGE:
					me->CastSpell(me, RAID_MODE(SPELL_POWER_SURGE_10, SPELL_POWER_SURGE_25), false);
					events.RepeatEvent(19000);
					break;
				case EVENT_MINION_SPELL_STATIC_FIELD:
					me->CastSpell(me, RAID_MODE(SPELL_STATIC_FIELD_10, SPELL_STATIC_FIELD_25), false);
					events.RepeatEvent(3000);
					break;
				case EVENT_MINION_SPELL_MAGNETIC_PULL:
					events.RepeatEvent(25000);
					if (pInstance)
						if (Creature* feugen = ObjectAccessor::GetCreature(*me, pInstance->GetData64(DATA_FEUGEN_BOSS)))
						{
							if (!feugen->IsAlive() || !feugen->GetVictim() || !me->GetVictim())
								return;

							float threatFeugen = feugen->getThreatManager().getThreat(feugen->GetVictim());
							float threatStalagg = me->getThreatManager().getThreat(me->GetVictim());
							Unit* tankFeugen = feugen->GetVictim();
							Unit* tankStalagg = me->GetVictim();

							feugen->getThreatManager().modifyThreatPercent(tankFeugen, -100);
							feugen->AddThreat(tankStalagg, threatFeugen);
							feugen->CastSpell(tankStalagg, SPELL_MAGNETIC_PULL, true);
							feugen->AI()->DoAction(ACTION_MAGNETIC_PULL);

							me->getThreatManager().modifyThreatPercent(tankStalagg, -100);
							me->AddThreat(tankFeugen, threatStalagg);
							me->CastSpell(tankFeugen, SPELL_MAGNETIC_PULL, true);
							DoAction(ACTION_MAGNETIC_PULL);
						}
					
					break;
				case EVENT_MINION_CHECK_DISTANCE:
					if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
					{
						if (me->GetExactDist(cr) > 60.0f || me->GetExactDist(cr) < 20.0f)
						{
							cr->InterruptNonMeleeSpells(true);
							cr->CastSpell(me->GetVictim(), SPELL_TESLA_SHOCK, true); // dont know real spell
							events.RepeatEvent(1500);
							break;
						}
						else
						{
							cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
						}
					}

					events.RepeatEvent(5000);
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

class spell_thaddius_pos_neg_charge : public SpellScriptLoader
{
    public:
        spell_thaddius_pos_neg_charge() : SpellScriptLoader("spell_thaddius_pos_neg_charge") { }

        class spell_thaddius_pos_neg_charge_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_thaddius_pos_neg_charge_SpellScript);

            void HandleTargets(std::list<WorldObject*>& targets)
            {
                uint8 count = 0;
                for (std::list<WorldObject*>::iterator ihit = targets.begin(); ihit != targets.end(); ++ihit)
                    if ((*ihit)->GetGUID() != GetCaster()->GetGUID())
                        if (Player* target = (*ihit)->ToPlayer())
                            if (target->HasAura(GetTriggeringSpell()->Id))
                                ++count;

                if (count)
                {
                    uint32 spellId = 0;

                    if (GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE)
                        spellId = SPELL_POSITIVE_CHARGE_STACK;
                    else // if (GetSpellInfo()->Id == SPELL_NEGATIVE_CHARGE)
                        spellId = SPELL_NEGATIVE_CHARGE_STACK;

                    GetCaster()->SetAuraStack(spellId, GetCaster(), count);
                }
            }

            void HandleDamage(SpellEffIndex /*effIndex*/)
            {
                if (!GetTriggeringSpell())
                    return;

                Unit* target = GetHitUnit();
				if (!target)
					return;

                if (target->HasAura(GetTriggeringSpell()->Id) || target->GetTypeId() != TYPEID_PLAYER)
                    SetHitDamage(0);
                else if (target->GetInstanceScript())
					target->GetInstanceScript()->SetData(DATA_CHARGES_CROSSED, 0);
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_thaddius_pos_neg_charge_SpellScript();
        }
};

class spell_thaddius_polarity_shift : public SpellScriptLoader
{
    public:
        spell_thaddius_polarity_shift() : SpellScriptLoader("spell_thaddius_polarity_shift") { }

        class spell_thaddius_polarity_shift_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_thaddius_polarity_shift_SpellScript);

            bool Validate(SpellInfo const* /*spell*/)
            {
                if (!sSpellMgr->GetSpellInfo(SPELL_POSITIVE_POLARITY) || !sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_POLARITY))
                    return false;
                return true;
            }

            void HandleDummy(SpellEffIndex /* effIndex */)
            {
                Unit* caster = GetCaster();
                if (Unit* target = GetHitUnit())
                    target->CastSpell(target, roll_chance_i(50) ? SPELL_POSITIVE_POLARITY : SPELL_NEGATIVE_POLARITY, true, NULL, NULL, caster->GetGUID());
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_thaddius_polarity_shift_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_thaddius_polarity_shift_SpellScript();
        }
};

void AddSC_boss_thaddius()
{
    new boss_thaddius();
    new boss_thaddius_summon();

	new spell_thaddius_pos_neg_charge();
    new spell_thaddius_polarity_shift();
}
